//=============================================================================

#include <math.h>
#include "../inc/aAux.h"

//=============================================================================

aAux::aAux()
{
  pi = 3.1415926535f;
  pid = 0.017453292f;
  pidi = 57.2957795f;
}

//=============================================================================

aVector aAux::aaCrossMulty(const aVector& vec, const aVector& vec2)
{
  aVector res;

  res.x = (vec.y*vec2.z) - (vec.z*vec2.y);
  res.y = (vec.z*vec2.x) - (vec.x*vec2.z);
  res.z = (vec.x*vec2.y) - (vec.y*vec2.x);

  return res;
}

//=============================================================================

aVector aAux::aaNormalize(const aVector& v)
{
  aVector vec(v);
  float m = aaGetMagnitude(vec);

  if (aaCompareFloat(m, 0.0f))
    return aVector(0.0f, 0.0f, 0.0f);

  vec.x /= m;
  vec.y /= m;
  vec.z /= m;

  return vec;
}

//=============================================================================

float aAux::aaGetAngle(const aVector& v, const aVector& v2)
{
  aVector vec(aaNormalize(v));
  aVector vec2(aaNormalize(v2));

  float dot = aaDotMulty(vec, vec2);

  if (dot > 1.0)
    dot = 1.0;
  if (dot < (-1.0))
    dot = -1.0;

  return (acos(dot)/**180.0/pi*/);
}

//=============================================================================

aVector aAux::aaRotateAroundVector(const aVector& v, const aVector& v2, float angle)
{
  aVector dx, dy, dz;
  aVector vec(v);
  aVector vec2(v2);
  float dot = aaDotMulty(vec, vec2);
  float angle2 = ator(angle);

  dz.x = vec2.x * dot;
  dz.y = vec2.y * dot;
  dz.z = vec2.z * dot;

  dx.x = vec.x - dz.x;
  dx.y = vec.y - dz.y;
  dx.z = vec.z - dz.z;

  dy = aaCrossMulty(dx, vec2);

  vec.x = dx.x*cos(angle2) + dy.x*sin(angle2) + dz.x;
  vec.y = dx.y*cos(angle2) + dy.y*sin(angle2) + dz.y;
  vec.z = dx.z*cos(angle2) + dy.z*sin(angle2) + dz.z;

  return vec;
}

//=============================================================================

aVector aAux::aaSubtrack(const aVector& v, const aVector& vec2)
{
  aVector vec(v);

  vec.x -= vec2.x;
  vec.y -= vec2.y;
  vec.z -= vec2.z;

  return vec;
}

//=============================================================================

aVector aAux::aaAdd(const aVector& v, const aVector& vec2)
{
  aVector vec(v);

  vec.x += vec2.x;
  vec.y += vec2.y;
  vec.z += vec2.z;

  return vec;
}

//=============================================================================

aVector aAux::aaAdd(const aVector& v, float num)
{
  aVector vec(v);

  vec.x += num;
  vec.y += num;
  vec.z += num;

  return vec;
}

//=============================================================================

bool aAux::aaCompareFloat(float f1, float f2)
{
  if (aaFloatAbs((f1) - (f2)) < 0.0001f)
    return 1;

  return 0;
}

//=============================================================================

bool aAux::aaCompareFloatHigh(float f1, float f2)
{
  if (aaFloatAbs(f1 - f2) < 0.00001f)
    return 1;

  return 0;
}

//=============================================================================

float aAux::aaGetLength(const aVector& vec, const aVector& vec2)
{
  float vx = vec2.x - vec.x;
  float vy = vec2.y - vec.y;
  float vz = vec2.z - vec.z;

  return sqrt(vx*vx + vy*vy + vz*vz);
}

//=============================================================================

aVector aAux::aaMultiply(const aVector& v, float num)
{
  aVector vec(v);
  vec.x *= num;
  vec.y *= num;
  vec.z *= num;

  return vec;
}

//=============================================================================

aVector aAux::aaDiv(const aVector& v, float num)
{
  aVector vec(v);

  if (aaCompareFloat(num, 0.0))
    return aVector(0.0, 0.0, 0.0);

  vec.x /= num;
  vec.y /= num;
  vec.z /= num;

  return vec;
}

//=============================================================================

bool aAux::aaCheckVectorIn(const aVector& p, const aVector& vec1, const aVector& vec2, const aVector& vec3)
{
  aVector tmpVector2, tmpVector3, tmpVector4;

  tmpVector2 = aAux::aaGetInstance().aaSubtrack(vec1, p);
  tmpVector3 = aAux::aaGetInstance().aaSubtrack(vec2, p);
  tmpVector4 = aAux::aaGetInstance().aaSubtrack(vec3, p);

  float angle = aAux::aaGetInstance().aaGetAngle(tmpVector2, tmpVector3)*aAux::aaGetInstance().aaGetPidi();

  angle += aAux::aaGetInstance().aaGetAngle(tmpVector3, tmpVector4)*aAux::aaGetInstance().aaGetPidi();
  angle += aAux::aaGetInstance().aaGetAngle(tmpVector4, tmpVector2)*aAux::aaGetInstance().aaGetPidi();

  if ((angle >= 359.9f) && (angle <= 360.1f))
    return 1;
  //else
  //  if (angle > 360.0f)
  //    return 1;

  return 0;
}

//=============================================================================

aVector aAux::aaGetIntersection(const aVector& l1, const aVector& l2, const aVector& s1, const aVector& s2, const aVector& s3)
{
  aVector tmpVector = aaGetNormal(s1, s2, s3);

  float r1 = aaDotMulty(tmpVector, aaSubtrack(l1, s1));
  float r2 = aaDotMulty(tmpVector, aaSubtrack(l2, s1));

  tmpVector = aaAdd(l1, aaMultiply(aaSubtrack(l2, l1), -r1/(r2 - r1)));

  if (aaVectorOnTriangleEdges(tmpVector, s1, s2, s3))
    return tmpVector; 

  if ((r1*r2) < 0.0) // have some interscetion
    return tmpVector;

  return aVector(0.0f, 0.0f, 0.0f);
}

//=============================================================================

void aAux::aaSetVertexesAsClockwise(aList<aVector>& list)
{
  class tmpMass
  {
    public:
      const tmpMass& operator=(const tmpMass& m)
      {
        this->vec = m.vec;
        this->tan = m.tan;

        return (*this);
      }
      
      aVector vec;
      float tan;
  }* mass;

  unsigned int c = list.alGetCount();
  mass = new tmpMass[c];

  aItem<aVector>* tmpItem = list.alGetFirstItem();
  unsigned int i, j;
  float maxX = tmpItem->data.x, maxY = tmpItem->data.y, minX = tmpItem->data.x, minY = tmpItem->data.y, originX, originY;

  tmpItem = tmpItem->next;

  while(tmpItem)
  {
    if (maxX < tmpItem->data.x)
      maxX = tmpItem->data.x;
    if (minX > tmpItem->data.x)
      minX = tmpItem->data.x;

    if (maxY < tmpItem->data.y)
      maxY = tmpItem->data.y;
    if (minY > tmpItem->data.y)
      minY = tmpItem->data.y;
    tmpItem = tmpItem->next;
  }

  originX = minX + (maxX - minX)/2;
  originY = minY + (maxY - minY)/2;

  i = 0;
  tmpItem = list.alGetFirstItem();
  while(tmpItem)
  {
    mass[i].vec = tmpItem->data;
    mass[i].tan = atan2(mass[i].vec.y - originY, mass[i].vec.x - originX);
    ++i;
    tmpItem = tmpItem->next;
  }

  tmpMass tmpVector;

  for (i = 0; i < c - 1; ++i)
  {
    if (mass[i].tan > mass[i + 1].tan)
    {
      tmpVector = mass[i];
      mass[i] = mass[i + 1];
      mass[i + 1] = tmpVector;
    }
    for (j = i; j > 0; --j)
    {
      if (mass[j].tan < mass[j - 1].tan)
      {
        tmpVector = mass[j - 1];
        mass[j - 1] = mass[j];
        mass[j] = tmpVector;
      }
    }
  }

  aItem<aVector>* tmpItem2 = list.alGetFirstItem();
  i = 0;

  while(tmpItem2)
  {
    tmpItem2->data = mass[i++].vec;
    tmpItem2 = tmpItem2->next;
  }

  delete [] mass;
}

//=============================================================================

void aAux::aaSetVertexesAsClockwise(aList<aVector>& list, const aVector& i, const aVector& j, const aVector& k, const aVector& o)
{
  class tmpMass
  {
    public:
      const tmpMass& operator=(const tmpMass& m)
      {
        this->vec = m.vec;
        this->tan = m.tan;

        return (*this);
      }
      
      aVector vec;
      float tan;
  }* mass;

  unsigned int c = list.alGetCount();
  mass = new tmpMass[c];

  aItem<aVector>* tmpItem = list.alGetFirstItem();
  unsigned int ii, jj;
  aVector tmpVector = aAux::aaGetInstance().aaSubtrack(tmpItem->data, o);
  tmpVector = aAux::aaGetInstance().aaVVVXYZ2pos(i, j, k, tmpVector);
  float maxX = tmpVector.x, maxY = tmpVector.y, minX = tmpVector.x, minY = tmpVector.y, originX, originY;

  tmpItem = tmpItem->next;

  while(tmpItem)
  {
    tmpVector = aAux::aaGetInstance().aaSubtrack(tmpItem->data, o);
    tmpVector = aAux::aaGetInstance().aaVVVXYZ2pos(i, j, k, tmpVector);
    if (maxX < tmpVector.x)
      maxX = tmpVector.x;
    if (minX > tmpVector.x)
      minX = tmpVector.x;

    if (maxY < tmpVector.y)
      maxY = tmpVector.y;
    if (minY > tmpVector.y)
      minY = tmpVector.y;
    tmpItem = tmpItem->next;
  }

  originX = minX + (maxX - minX)/2;
  originY = minY + (maxY - minY)/2;

  ii = 0;
  tmpItem = list.alGetFirstItem();
  while(tmpItem)
  {
    mass[ii].vec = tmpItem->data;
    tmpVector = aAux::aaGetInstance().aaSubtrack(tmpItem->data, o);
    tmpVector = aAux::aaGetInstance().aaVVVXYZ2pos(i, j, k, tmpVector);
    mass[ii].tan = atan2(tmpVector.y - originY, tmpVector.x - originX);
    ++ii;
    tmpItem = tmpItem->next;
  }

  tmpMass tmpVector2;

  for (ii = 0; ii < c - 1; ++ii)
  {
    if (mass[ii].tan > mass[ii + 1].tan)
    {
      tmpVector2 = mass[ii];
      mass[ii] = mass[ii + 1];
      mass[ii + 1] = tmpVector2;
    }
    for (jj = ii; jj > 0; --jj)
    {
      if (mass[jj].tan < mass[jj - 1].tan)
      {
        tmpVector2 = mass[jj - 1];
        mass[jj - 1] = mass[jj];
        mass[jj] = tmpVector2;
      }
    }
  }

  aItem<aVector>* tmpItem2 = list.alGetFirstItem();
  ii = 0;

  while(tmpItem2)
  {
    tmpItem2->data = mass[ii++].vec;
    tmpItem2 = tmpItem2->next;
  }

  delete [] mass;
}

//=============================================================================
//
// "...Will return the correct vector only if point of
// intersection is really on both lines lies..." - he said.
//

aVector aAux::aaGetLinesIntersection(const aVector& p11, const aVector& p12, const aVector& p21, const aVector& p22, const aVector& n)
{
  aVector tmpVector = aaGetIntersection(p21, p22, p11, p12, n);

  if (!aaCompareFloat(aaGetMagnitude(tmpVector), 0.0f))
  {
    float f1 = aaGetLength(tmpVector, p11);
    float f2 = aaGetLength(tmpVector, p12);
    float f3 = aaGetLength(p12, p11);

    if (aaCompareFloat(f1 + f2, f3))
    {
      f1 = aaGetLength(tmpVector, p21);
      f2 = aaGetLength(tmpVector, p22);
      f3 = aaGetLength(p22, p21);
          
      if (aaCompareFloat(f1 + f2, f3))
        return tmpVector;
    }
  }

  return aVector(0.0, 0.0, 0.0);
}

//=============================================================================

aVector aAux::aaGetClosestPoint(const aVector& p1, const aVector& p2, const aVector& wp)
{
  aVector tmpVector = aaSubtrack(wp, p1);
  aVector tmpVector2 = aaNormalize(aaSubtrack(p2, p1));
  float tmpMagnitude = aaGetLength(p1, p2);
  float tmpDot = aaDotMulty(tmpVector2, tmpVector);

  if (tmpDot <= 0)
    return p1;

  if (tmpDot >= tmpMagnitude)
    return p2;

  return aaAdd(p1, aaMultiply(tmpVector2, tmpDot));
}

//=============================================================================

bool aAux::aaGetSamePoints(const aVector& p11, const aVector& p12, const aVector& p21, const aVector& p22)
{
  if ((p11 == p21) || (p11 == p22) || (p12 == p21) || (p12 == p22))
    return 1;
  else
    return 0;
}

//=============================================================================

bool aAux::aaVectorOnTriangleEdges(const aVector& p, const aVector& vec1, const aVector& vec2, const aVector& vec3)
{
  if (aaVectorOnLine(p, vec1, vec2) || aaVectorOnLine(p, vec2, vec3) || aaVectorOnLine(p, vec3, vec1))
    return 1;

  return 0;
}

//=============================================================================

aAux::~aAux()
{
}

//=============================================================================
